/**
 * 
 */
package org.metaverse.h2;

import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.Date;
import java.util.Iterator;

import javax.sql.DataSource;

import org.metaverse.h2.cache.ValueCache;
import org.metaverse.h2.sql.SQL;
import org.metaverse.warehouse.Senary;
import org.metaverse.warehouse.Uploader;
import org.metaverse.warehouse.WarehouseException;
import org.metaverse.warehouse.util.RDFParseUtil;
import org.metaverse.warehouse.util.SenaryImpl;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.URIImpl;
import org.singularity.io.DME;
import org.singularity.io.Pipe;
import org.singularity.io.DME.Code;
import org.singularity.io.util.TPipe;

/**
 * @author vjache
 *
 */
public class UploaderImpl implements Uploader {
	
	private static final URI DEFAULT_USER = new URIImpl("http://www.metaverse.org/users/vjache");
	private static final URI DEFAULT_USER_CONTEXT = new URIImpl("http://www.metaverse.org/users/vjache/def-context");

	private final DataSource _connPool;
	private final ValueCache _valueCache;
	private final SQL _sql;

	/**
	 * 
	 */
	public UploaderImpl(SQL aSql, DataSource aDataSource,ValueCache aValueCache) {
		_connPool = aDataSource;
		_valueCache = aValueCache;
		_sql = aSql;
	}

	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Uploader#assertStatements(java.lang.Iterable, org.openrdf.model.Resource[])
	 */
	@Override
	public void assertStatements(Iterator<Statement> aStatements,
			Resource... aContexts) throws WarehouseException
	{
		apply(DME.Code.Add, aStatements, aContexts);
	}

	private void apply(DME.Code aCode, Iterator<Statement> aStatements, Resource... aContexts)
			throws WarehouseException {
		SenaryMainPipe pipe = null;
		final ValueFactory vf = getValueFactory();
		final Date lat = new Date();
		try {
			pipe = createSenaryPipe(aCode);
			while(aStatements.hasNext())
			{
				final Statement stmt = aStatements.next();
				pushSenary(pipe, vf, lat, stmt, aContexts);
			}
		} catch (Exception e) {
			throw new WarehouseException(e);
		}
		finally
		{
			if(pipe!=null)
				try {
					pipe.close();
				} catch (IOException e) 
				{throw new WarehouseException(e);}
		}
	}

	private void pushSenary(
			Pipe<Senary> pipe, 
			ValueFactory vf,
			Date lat, 
			Statement stmt, Resource... aContexts)
			throws IOException {
		final Resource subject = stmt.getSubject();
		final URI predicate = stmt.getPredicate();
		final Value object = stmt.getObject();
		if(aContexts.length>0)
			for(Resource ctx:aContexts)
			{
				pipe.push(
						new SenaryImpl(
								subject, 
								predicate, 
								object, 
								ctx, 
								DEFAULT_USER, 
								lat, 
								vf));
			}
		else {
			Resource context = stmt.getContext();
			pipe.push(
					new SenaryImpl(
							subject, 
							predicate, 
							object, 
							context==null?DEFAULT_USER_CONTEXT:context, 
							DEFAULT_USER, 
							lat, 
							vf));
		}
	}

	private SenaryMainPipe createSenaryPipe(DME.Code aOpCode) throws SQLException {
		return new SenaryMainPipe(_connPool, 1000, _sql, _valueCache,aOpCode);
	}

	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Uploader#getValueFactory()
	 */
	@Override
	public final ValueFactory getValueFactory() {
		return _valueCache.getValueFactory();
	}

	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Uploader#retireStatements(java.lang.Iterable, org.openrdf.model.Resource[])
	 */
	@Override
	public void retireStatements(Iterator<Statement> aStatements,
			Resource... aContexts) throws WarehouseException
	{
		apply(DME.Code.Remove, aStatements, aContexts);
	}

	@Override
	public void assertStatements(URL aUrl, final Resource... aContexts)
			throws WarehouseException {
		apply(DME.Code.Add, aUrl, aContexts);
	}

	private void apply(final Code opCode, URL aUrl, final Resource... aContexts)
			throws WarehouseException {
		SenaryMainPipe pipe = null;
		try
		{
			final Date lat = new Date();
			final ValueFactory vf = getValueFactory();
			pipe = createSenaryPipe(opCode);
			RDFParseUtil.createTask(
					aUrl, 
					new TPipe<Statement, Senary>(pipe) {
						@Override
						public void push(Statement aItem) throws IOException {
							pushSenary(_next, vf, lat, aItem, aContexts);
						}
					}).run();
		}
		catch(SQLException e)
		{
			throw new WarehouseException(e);
		}
//		finally
//		{
//			if(pipe!=null)
//				try {
//					pipe.close();
//				} catch (IOException e) {
//					throw new WarehouseException(e);
//				}
//		}
	}

	@Override
	public void retireStatements(URL aUrl, Resource... aContexts)
			throws WarehouseException {
		apply(DME.Code.Remove, aUrl, aContexts);
	}

}
